﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ApiDoc.CodeSyntaxGenerators.Languages
{
    /// <summary>
    /// VB.NET language generator for the Api Documentation.
    /// </summary>
    [CodeSyntaxGenerator("VB.NET")]
    public class VBNetLanguageSyntaxGenerator : CodeSyntaxGeneratorBase
    {
        public override string GenerateName(Type type)
        {
            var ret = new StringBuilder();

            if (type.IsGenericTypeDefinition) {
                ret.AppendFormat("{0}(Of ", type.Name.Substring(0, type.Name.IndexOf('`')));
                ret.Append(GenerateGenericArgumentList(type.GetGenericArguments()));
                ret.Append(")");
            } else {
                ret.Append(type.Name);
            }

            return ret.ToString();
        }

        public override string GenerateName(System.Reflection.MethodInfo method)
        {
            var ret = new StringBuilder();

            ret.Append(method.Name);

            // Add generic parameters
            if (method.ContainsGenericParameters) {
                ret.Append("{0}(Of ");
                ret.Append(GenerateGenericArgumentList(method.GetGenericArguments()));
                ret.Append(")");
            }

            // Add parameters
            ret.Append("(");
            ret.Append(GenerateParameterList(method.GetParameters(), includeNames: false));
            ret.Append(")");

            return ret.ToString();
        }

        public override string GenerateName(System.Reflection.PropertyInfo property)
        {
            var ret = new StringBuilder();

            ret.Append(property.Name);

            return ret.ToString();
        }

        public override string GenerateName(System.Reflection.ConstructorInfo constructor)
        {
            var ret = new StringBuilder();

            if (constructor.ReflectedType.IsGenericType) {
                ret.Append(constructor.ReflectedType.Name.Substring(0, constructor.ReflectedType.Name.IndexOf('`')));
            } else {
                ret.Append(constructor.ReflectedType.Name);
            }

            //// Add generic parameters.
            //if (constructor.ContainsGenericParameters) {
            //    ret.Append("(Of ");
            //    ret.Append(GenerateGenericArgumentList(constructor.GetGenericArguments()));
            //    ret.Append(")");
            //}

            // Add parameters
            ret.Append("(");
            ret.Append(GenerateParameterList(constructor.GetParameters(), includeNames: false));
            ret.Append(")");

            return ret.ToString();
        }

        public override string GenerateName(System.Reflection.FieldInfo field)
        {
            var ret = new StringBuilder();

            ret.Append(field.Name);

            return ret.ToString();
        }

        public override string GenerateName(System.Reflection.EventInfo eventx)
        {
            var ret = new StringBuilder();

            ret.Append(eventx.Name);

            return ret.ToString();
        }

        public override string GenerateName(System.Reflection.ParameterInfo parameter)
        {
            var ret = new StringBuilder();

            ret.Append(parameter.Name);

            return ret.ToString();
        }

        public override string GenerateCodeDefinition(Type type)
        {
            var ret = new StringBuilder();

            //// Attributes
            //foreach (System.Attribute attribute in m_element.GetCustomAttributes(false)) {
            //    // TODO
            //}

            // Scope modifier.
            if (type.IsPublic) {
                ret.Append("Public ");
            } else {
                ret.Append("Protected ");
            }

            // TODO static.

            // Extra modifiers.
            if (type.IsAbstract) {
                ret.Append("Abstract ");
            }
            if (type.IsSealed) {
                ret.Append("Sealed ");
            }

            // Type of Type
            if (type.IsClass) {
                ret.Append("Class ");
                //} else if (m_element.IsDelegate) {
                //    ret.Append("delegate ");
            } else if (type.IsEnum) {
                ret.Append("Enum ");
            } else if (type.IsInterface) {
                ret.Append("Interface ");
            }

            // Class name in friendly form
            ret.Append(GenerateName(type));

            // Base class
            bool needComma = false;
            if (type.BaseType != null) {
                ret.AppendFormat(" Implements {0}", GenerateName(type.BaseType));
                needComma = true;
            }

            var interfaces = type.GetInterfaces();
            for (int i = 0; i < interfaces.Count(); ++i) {
                if (needComma) {
                    ret.Append(", ");
                } else {
                    ret.Append(" Implements ");
                }

                ret.Append(GenerateName(interfaces[i]));
            }

            // TODO generic type constraints.

            return ret.ToString();
        }

        public override string GenerateCodeDefinition(System.Reflection.MethodInfo method)
        {
            var ret = new StringBuilder();

            // Sub or Function
            if (method.ReturnType == null) {
                ret.Append("Sub ");
            } else {
                ret.Append("Function ");
            }

            ret.Append(method.Name);

            // Add generic parameters
            if (method.ContainsGenericParameters) {
                ret.Append("{0}(Of ");
                ret.Append(GenerateGenericArgumentList(method.GetGenericArguments()));
                ret.Append(")");
            }

            // Add parameters
            ret.Append("(");
            ret.Append(GenerateParameterList(method.GetParameters(), includeNames: true));
            ret.Append(")");

            // Return type
            if (method.ReturnType != null) {
                ret.Append(" As ");
                ret.Append(GenerateName(method.ReturnType));
            }

            return ret.ToString();
        }

        public override string GenerateCodeDefinition(System.Reflection.PropertyInfo property)
        {
            var ret = new StringBuilder();

            // Property
            ret.Append("Property ");

            ret.Append(property.Name);

            // Property Type
            ret.Append(" As ");
            ret.Append(GenerateName(property.PropertyType));

            return ret.ToString();
        }

        public override string GenerateCodeDefinition(System.Reflection.ConstructorInfo constructor)
        {
            var ret = new StringBuilder();

            if (constructor.ReflectedType.IsGenericType) {
                ret.Append(constructor.ReflectedType.Name.Substring(0, constructor.ReflectedType.Name.IndexOf('`')));
            } else {
                ret.Append(constructor.ReflectedType.Name);
            }

            //// Add generic parameters.
            //if (constructor.ContainsGenericParameters) {
            //    ret.Append("(Of ");
            //    ret.Append(GenerateGenericArgumentList(constructor.GetGenericArguments()));
            //    ret.Append(")");
            //}

            // Add parameters
            ret.Append("(");
            ret.Append(GenerateParameterList(constructor.GetParameters(), includeNames: true));
            ret.Append(")");

            return ret.ToString();
        }

        public override string GenerateCodeDefinition(System.Reflection.FieldInfo field)
        {
            var ret = new StringBuilder();

            // Property
            ret.Append("Dim ");

            ret.Append(field.Name);

            // Property Type
            ret.Append(" As ");
            ret.Append(GenerateName(field.FieldType));

            return ret.ToString();
        }

        public override string GenerateCodeDefinition(System.Reflection.EventInfo eventx)
        {
            var ret = new StringBuilder();

            // Property
            ret.Append("Event ");

            ret.Append(eventx.Name);

            // Property Type
            ret.Append(" As ");
            ret.Append(GenerateName(eventx.EventHandlerType));

            return ret.ToString();
        }

        public override string GenerateCodeDefinition(System.Reflection.ParameterInfo parameter)
        {
            return GenerateCodeDefinition(parameter, includeName: true);
        }

        protected string GenerateCodeDefinition(System.Reflection.ParameterInfo parameter, bool includeName)
        {
            var ret = new StringBuilder();

            if ((parameter.Attributes & ParameterAttributes.Out) != 0) {
                ret.Append("out ");
            }

            if ((parameter.Attributes & ParameterAttributes.In) != 0) {
                ret.Append("ref ");
            }

            ret.Append(GenerateName(parameter.ParameterType));

            if (includeName) {
                ret.Append(parameter.Name);
            }

            if (parameter.IsOptional) {
                if (parameter.DefaultValue == null) {
                    ret.Append("= Nothing");
                } else if (parameter.ParameterType == typeof(string)) {
                    ret.AppendFormat("= \"{0}\"", parameter.DefaultValue.ToString());
                } else {
                    ret.AppendFormat("= {0}", parameter.DefaultValue.ToString());
                }
            }

            return ret.ToString();
        }

        /// <summary>
        /// Generates a generic argument list for a syntax.
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        protected string GenerateGenericArgumentList(Type[] arguments)
        {
            var ret = new StringBuilder();

            for (int i = 0; i < arguments.Count(); ++i) {
                ret.Append(arguments[i].Name);

                if (i != arguments.Count() - 1) {
                    ret.Append(", ");
                }
            }

            return ret.ToString();
        }

        /// <summary>
        /// Generates a parameter list for a syntax.
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        protected string GenerateParameterList(ParameterInfo[] parameters, bool includeNames)
        {
            var ret = new StringBuilder();

            for (int i = 0; i < parameters.Count(); ++i) {
                var parameter = parameters[i];

                ret.Append(GenerateCodeDefinition(parameter, includeName: includeNames));

                if (i != parameters.Count() - 1) {
                    ret.Append(", ");
                }
            }

            return ret.ToString();
        }
    }
}
